import React, { Component } from "react";

import { Form, Input, Select, InputNumber, Switch, Row, Col, Button, Tabs, Card } from "antd";
import { MinusCircleOutlined, PlusOutlined } from "@ant-design/icons";

import { DeviceProfileTemplate } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_template_pb";
import { CodecRuntime, Measurement, MeasurementKind } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";
import { Region, MacVersion, RegParamsRevision } from "@chirpstack/chirpstack-api-grpc-web/common/common_pb";
import { ListDeviceProfileAdrAlgorithmsResponse } from "@chirpstack/chirpstack-api-grpc-web/api/device_profile_pb";

import DeviceProfileStore from "../../stores/DeviceProfileStore";
import CodeEditor from "../../components/CodeEditor";

interface IProps {
  initialValues: DeviceProfileTemplate;
  onFinish: (obj: DeviceProfileTemplate) => void;
  update?: boolean;
}

interface IState {
  supportsOtaa: boolean;
  supportsClassB: boolean;
  supportsClassC: boolean;
  payloadCodecRuntime: CodecRuntime;
  adrAlgorithms: [string, string][];
}

class DeviceProfileTemplateForm extends Component<IProps, IState> {
  formRef = React.createRef<any>();

  constructor(props: IProps) {
    super(props);
    this.state = {
      supportsOtaa: false,
      supportsClassB: false,
      supportsClassC: false,
      payloadCodecRuntime: CodecRuntime.NONE,
      adrAlgorithms: [],
    };
  }

  componentDidMount() {
    const v = this.props.initialValues;

    this.setState({
      supportsOtaa: v.getSupportsOtaa(),
      supportsClassB: v.getSupportsClassB(),
      supportsClassC: v.getSupportsClassC(),
      payloadCodecRuntime: v.getPayloadCodecRuntime(),
    });

    DeviceProfileStore.listAdrAlgorithms((resp: ListDeviceProfileAdrAlgorithmsResponse) => {
      let adrAlgorithms: [string, string][] = [];
      for (const a of resp.getResultList()) {
        adrAlgorithms.push([a.getId(), a.getName()]);
      }

      this.setState({
        adrAlgorithms: adrAlgorithms,
      });
    });
  }

  onFinish = (values: DeviceProfileTemplate.AsObject) => {
    const v = Object.assign(this.props.initialValues.toObject(), values);
    let dp = new DeviceProfileTemplate();
    dp.setId(v.id);

    dp.setName(v.name);
    dp.setDescription(v.description);
    dp.setVendor(v.vendor);
    dp.setFirmware(v.firmware);
    dp.setRegion(v.region);
    dp.setMacVersion(v.macVersion);
    dp.setRegParamsRevision(v.regParamsRevision);
    dp.setAdrAlgorithmId(v.adrAlgorithmId);
    dp.setFlushQueueOnActivate(v.flushQueueOnActivate);
    dp.setUplinkInterval(v.uplinkInterval);
    dp.setDeviceStatusReqInterval(v.deviceStatusReqInterval);

    // join otaa /abp
    dp.setSupportsOtaa(v.supportsOtaa);
    dp.setAbpRx1Delay(v.abpRx1Delay);
    dp.setAbpRx1DrOffset(v.abpRx1DrOffset);
    dp.setAbpRx2Dr(v.abpRx2Dr);
    dp.setAbpRx2Freq(v.abpRx2Freq);

    // class-b
    dp.setSupportsClassB(v.supportsClassB);
    dp.setClassBTimeout(v.classBTimeout);
    dp.setClassBPingSlotNbK(v.classBPingSlotNbK);
    dp.setClassBPingSlotDr(v.classBPingSlotDr);
    dp.setClassBPingSlotFreq(v.classBPingSlotFreq);

    // class-c
    dp.setSupportsClassC(v.supportsClassC);
    dp.setClassCTimeout(v.classCTimeout);

    // codec
    dp.setPayloadCodecRuntime(v.payloadCodecRuntime);
    dp.setPayloadCodecScript(v.payloadCodecScript);

    // tags
    for (const elm of v.tagsMap) {
      dp.getTagsMap().set(elm[0], elm[1]);
    }

    // measurements
    for (const elm of v.measurementsMap) {
      let m = new Measurement();
      m.setKind(elm[1].kind);
      m.setName(elm[1].name);
      dp.getMeasurementsMap().set(elm[0], m);
    }
    dp.setAutoDetectMeasurements(v.autoDetectMeasurements);

    this.props.onFinish(dp);
  };

  onSupportsOtaaChange = (checked: boolean) => {
    this.setState({
      supportsOtaa: checked,
    });
  };

  onSupportsClassBChnage = (checked: boolean) => {
    this.setState({
      supportsClassB: checked,
    });
  };

  onSupportsClassCChange = (checked: boolean) => {
    this.setState({
      supportsClassC: checked,
    });
  };

  onPayloadCodecRuntimeChange = (value: CodecRuntime) => {
    this.setState({
      payloadCodecRuntime: value,
    });
  };

  render() {
    const adrOptions = this.state.adrAlgorithms.map(v => <Select.Option value={v[0]}>{v[1]}</Select.Option>);

    return (
      <Form
        layout="vertical"
        initialValues={this.props.initialValues.toObject()}
        onFinish={this.onFinish}
        ref={this.formRef}
      >
        <Tabs>
          <Tabs.TabPane tab="常规项" key="1">
            <Form.Item
              label="编号"
              name="id"
              rules={[
                {
                  required: true,
                  pattern: new RegExp(/^[\w-]*$/g),
                  message: "请输入合法编号!",
                },
              ]}
            >
              <Input disabled={!!this.props.update} />
            </Form.Item>
            <Form.Item label="名称" name="name" rules={[{ required: true, message: "Please enter a name!" }]}>
              <Input />
            </Form.Item>
            <Form.Item label="供应商" name="vendor" rules={[{ required: true, message: "Please enter a vendor!" }]}>
              <Input />
            </Form.Item>
            <Form.Item
              label="固件版本"
              name="firmware"
              rules={[{ required: true, message: "Please enter a firmware version!" }]}
            >
              <Input />
            </Form.Item>
            <Form.Item label="描述" name="description">
              <Input.TextArea rows={6} />
            </Form.Item>
            <Form.Item label="区域协议" name="region" rules={[{ required: true, message: "Please select a region!" }]}>
              <Select>
                <Select.Option value={Region.AS923}>AS923</Select.Option>
                <Select.Option value={Region.AS923_2}>AS923-2</Select.Option>
                <Select.Option value={Region.AS923_3}>AS923-3</Select.Option>
                <Select.Option value={Region.AS923_4}>AS923-4</Select.Option>
                <Select.Option value={Region.AU915}>AU915</Select.Option>
                <Select.Option value={Region.CN779}>CN779</Select.Option>
                <Select.Option value={Region.EU433}>EU433</Select.Option>
                <Select.Option value={Region.EU868}>EU868</Select.Option>
                <Select.Option value={Region.IN865}>IN865</Select.Option>
                <Select.Option value={Region.ISM2400}>ISM2400</Select.Option>
                <Select.Option value={Region.KR920}>KR920</Select.Option>
                <Select.Option value={Region.RU864}>RU864</Select.Option>
                <Select.Option value={Region.US915}>US915</Select.Option>
              </Select>
            </Form.Item>
            <Row gutter={24}>
              <Col span={12}>
                <Form.Item
                  label="MAC版本"
                  tooltip="设备支持的LoRaWAN MAC版本。"
                  name="macVersion"
                  rules={[{ required: true, message: "请选择!" }]}
                >
                  <Select>
                    <Select.Option value={MacVersion.LORAWAN_1_0_0}>LoRaWAN 1.0.0</Select.Option>
                    <Select.Option value={MacVersion.LORAWAN_1_0_1}>LoRaWAN 1.0.1</Select.Option>
                    <Select.Option value={MacVersion.LORAWAN_1_0_2}>LoRaWAN 1.0.2</Select.Option>
                    <Select.Option value={MacVersion.LORAWAN_1_0_3}>LoRaWAN 1.0.3</Select.Option>
                    <Select.Option value={MacVersion.LORAWAN_1_0_4}>LoRaWAN 1.0.4</Select.Option>
                    <Select.Option value={MacVersion.LORAWAN_1_1_0}>LoRaWAN 1.1.0</Select.Option>
                  </Select>
                </Form.Item>
              </Col>
              <Col span={12}>
                <Form.Item
                  label="区域参数修订版本"
                  tooltip="设备支持的区域参数修订版本"
                  name="regParamsRevision"
                  rules={[{ required: true, message: "Please select a regional parameters revision!" }]}
                >
                  <Select>
                    <Select.Option value={RegParamsRevision.A}>A</Select.Option>
                    <Select.Option value={RegParamsRevision.B}>B</Select.Option>
                    <Select.Option value={RegParamsRevision.RP002_1_0_0}>RP002-1.0.0</Select.Option>
                    <Select.Option value={RegParamsRevision.RP002_1_0_1}>RP002-1.0.1</Select.Option>
                    <Select.Option value={RegParamsRevision.RP002_1_0_2}>RP002-1.0.2</Select.Option>
                    <Select.Option value={RegParamsRevision.RP002_1_0_3}>RP002-1.0.3</Select.Option>
                  </Select>
                </Form.Item>
              </Col>
            </Row>
            <Form.Item
              label="ADR算法"
              tooltip="用于控制设备数据速率的ADR算法。"
              name="adrAlgorithmId"
              rules={[{ required: true, message: "Please select an ADR algorithm!" }]}
            >
              <Select>{adrOptions}</Select>
            </Form.Item>
            <Row gutter={24}>
              <Col span={8}>
                <Form.Item
                  label="激活时刷新队列"
                  name="flushQueueOnActivate"
                  valuePropName="checked"
                  tooltip="如果启用，设备队列将在ABP或OTAA激活时刷新。"
                >
                  <Switch />
                </Form.Item>
              </Col>
              <Col span={8}>
                <Form.Item
                  label="预期上报周期（秒）"
                  tooltip="设备发送上行链路消息的预期间隔（秒）。这用于确定设备是活动的还是非活动的。"
                  name="uplinkInterval"
                  rules={[{ required: true, message: "Please enter an uplink interval!" }]}
                >
                  <InputNumber min={0} />
                </Form.Item>
              </Col>
              <Col span={8}>
                <Form.Item
                  label="设备状态请求频率（请求/天）"
                  tooltip="发起终端设备状态请求的频率（请求/天）。设置为0以禁用。"
                  name="deviceStatusReqInterval"
                >
                  <InputNumber min={0} />
                </Form.Item>
              </Col>
            </Row>
          </Tabs.TabPane>
          <Tabs.TabPane tab="入网方式(OTAA / ABP)" key="2">
            <Form.Item label="支持OTAA" name="supportsOtaa" valuePropName="checked">
              <Switch onChange={this.onSupportsOtaaChange} />
            </Form.Item>
            {!this.state.supportsOtaa && (
              <Row>
                <Col span={12}>
                  <Form.Item
                    label="RX1延迟"
                    name="abpRx1Delay"
                    rules={[{ required: true, message: "Please enter a RX1 delay!" }]}
                  >
                    <InputNumber min={0} max={15} />
                  </Form.Item>
                </Col>
                <Col span={12}>
                  <Form.Item
                    label="RX1速率偏移"
                    tooltip="有关有效值，请参阅LoRaWAN区域参数规范。"
                    name="abpRx1DrOffset"
                    rules={[{ required: true, message: "Please enter a RX1 data-rate offset!" }]}
                  >
                    <InputNumber min={0} max={15} />
                  </Form.Item>
                </Col>
              </Row>
            )}
            {!this.state.supportsOtaa && (
              <Row>
                <Col span={12}>
                  <Form.Item
                    label="RX2数据速率"
                    tooltip="有关有效值，请参阅LoRaWAN区域参数规范。"
                    name="abpRx2Dr"
                    rules={[{ required: true, message: "Please enter a RX2 data-rate!" }]}
                  >
                    <InputNumber min={0} max={15} />
                  </Form.Item>
                </Col>
                <Col span={12}>
                  <Form.Item
                    label="RX2 频率 (Hz)"
                    name="abpRx2Freq"
                    rules={[{ required: true, message: "Please enter a RX2 frequency!" }]}
                  >
                    <InputNumber min={0} style={{ width: "200px" }} />
                  </Form.Item>
                </Col>
              </Row>
            )}
          </Tabs.TabPane>
          <Tabs.TabPane tab="Class-B" key="3">
            <Form.Item label="支持Class-B" name="supportsClassB" valuePropName="checked">
              <Switch onChange={this.onSupportsClassBChnage} />
            </Form.Item>
            {this.state.supportsClassB && (
              <>
                <Row gutter={24}>
                  <Col span={12}>
                    <Form.Item
                      label="Class-B确认的下行链路超时（秒）"
                      tooltip="已确认下行链路传输的Class-B超时（秒）。"
                      name="classBTimeout"
                      rules={[{ required: true, message: "Please enter a Class-B confirmed downlink timeout!" }]}
                    >
                      <InputNumber min={0} />
                    </Form.Item>
                  </Col>
                  <Col span={12}>
                    <Form.Item
                      label="Class-B ping时隙周期"
                      tooltip="此值必须与设备的ping-slot周期相匹配。请参阅设备文档。"
                      name="classBPingSlotNbK"
                      rules={[{ required: true, message: "Please select the ping-slot periodicity!" }]}
                    >
                      <Select>
                        <Select.Option value={0}>Every second</Select.Option>
                        <Select.Option value={1}>Every 2 seconds</Select.Option>
                        <Select.Option value={2}>Every 4 seconds</Select.Option>
                        <Select.Option value={3}>Every 8 seconds</Select.Option>
                        <Select.Option value={4}>Every 16 seconds</Select.Option>
                        <Select.Option value={5}>Every 32 seconds</Select.Option>
                        <Select.Option value={6}>Every 64 seconds</Select.Option>
                        <Select.Option value={7}>Every 128 seconds</Select.Option>
                      </Select>
                    </Form.Item>
                  </Col>
                </Row>
                <Row gutter={24}>
                  <Col span={12}>
                    <Form.Item
                      label="Class-B ping-slot数据速率"
                      tooltip="此值必须与设备的ping插槽数据速率匹配。请参阅设备文档。"
                      name="classBPingSlotDr"
                      rules={[{ required: true, message: "Please enter the ping-slot data-rate!" }]}
                    >
                      <InputNumber min={0} />
                    </Form.Item>
                  </Col>
                  <Col span={12}>
                    <Form.Item
                      label="Class-B ping-slot频率 (Hz)"
                      tooltip="此值必须与设备的ping插槽屏绿匹配。请参阅设备文档。"
                      name="classBPingSlotFreq"
                      rules={[{ required: true, message: "Please enter the ping-slot frequency!" }]}
                    >
                      <InputNumber min={0} style={{ width: "200px" }} />
                    </Form.Item>
                  </Col>
                </Row>
              </>
            )}
          </Tabs.TabPane>
          <Tabs.TabPane tab="Class-C" key="4">
            <Form.Item label="支持Class-C" name="supportsClassC" valuePropName="checked">
              <Switch onChange={this.onSupportsClassCChange} />
            </Form.Item>
            {this.state.supportsClassC && (
              <Form.Item
                label="Class-C确认的下行链路超时（秒)"
                tooltip="已确认下行链路传输的Class-B超时（秒）。"
                name="classCTimeout"
                rules={[{ required: true, message: "Please enter a Class-C confirmed downlink timeout!" }]}
              >
                <InputNumber min={0} />
              </Form.Item>
            )}
          </Tabs.TabPane>
          <Tabs.TabPane tab="解析脚本" key="5">
            <Form.Item
              label="自定义解析"
              name="payloadCodecRuntime"
              tooltip="通过定义有效载荷编解码器，可以为您编码和解码二进制设备有效载荷。"
            >
              <Select onChange={this.onPayloadCodecRuntimeChange}>
                <Select.Option value={CodecRuntime.NONE}>None</Select.Option>
                <Select.Option value={CodecRuntime.CAYENNE_LPP}>Cayenne LPP</Select.Option>
                <Select.Option value={CodecRuntime.JS}>JavaScript functions</Select.Option>
              </Select>
            </Form.Item>
            {this.state.payloadCodecRuntime === CodecRuntime.JS && (
              <CodeEditor
                label="自定义解析函数"
                name="payloadCodecScript"
                value={this.formRef.current.getFieldValue("payloadCodecScript")}
                formRef={this.formRef}
              />
            )}
          </Tabs.TabPane>
          <Tabs.TabPane tab="标签" key="6">
            <Form.List name="tagsMap">
              {(fields, { add, remove }) => (
                <>
                  {fields.map(({ key, name, ...restField }) => (
                    <Row gutter={24}>
                      <Col span={6}>
                        <Form.Item
                          {...restField}
                          name={[name, 0]}
                          fieldKey={[name, 0]}
                          rules={[{ required: true, message: "Please enter a key!" }]}
                        >
                          <Input placeholder="Key" />
                        </Form.Item>
                      </Col>
                      <Col span={16}>
                        <Form.Item
                          {...restField}
                          name={[name, 1]}
                          fieldKey={[name, 1]}
                          rules={[{ required: true, message: "Please enter a value!" }]}
                        >
                          <Input placeholder="Value" />
                        </Form.Item>
                      </Col>
                      <Col span={2}>
                        <MinusCircleOutlined onClick={() => remove(name)} />
                      </Col>
                    </Row>
                  ))}
                  <Form.Item>
                    <Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
                      增加标签
                    </Button>
                  </Form.Item>
                </>
              )}
            </Form.List>
          </Tabs.TabPane>
          <Tabs.TabPane tab="测量" key="7">
            <Card bordered={false}>
              <p>
                ChirpStack可以在设备仪表板中聚合和可视化解码的设备测量值。要设置聚合设备度量，必须配置密钥、度量类型和名称（用户定义）。
                可以选择以下测量类型：
              </p>
              <ul>
                <li>
                  <strong>未知/未设置</strong>: 自动检测到的关键点的默认值。这将禁用此度量的聚合。
                </li>
                <li>
                  <strong>计数器</strong>: 用于连续递增计数器。
                </li>
                <li>
                  <strong>绝对值</strong>: 用于读取/上行时重置的计数器。
                </li>
                <li>
                  <strong>仪表</strong>: 温度、湿度、压力等。。。
                </li>
                <li>
                  <strong>字符串</strong>: 对于布尔值或字符串值。
                </li>
              </ul>
            </Card>
            <Form.Item
              label="自动检测测量键"
              name="autoDetectMeasurements"
              valuePropName="checked"
              tooltip="如果启用，将根据解码的有效载荷密钥自动添加测量密钥。如果解码的有效载荷包含随机密钥，则需要禁用自动检测。"
            >
              <Switch />
            </Form.Item>
            <Form.List name="measurementsMap">
              {(fields, { add, remove }) => (
                <>
                  {fields.map(({ key, name, ...restField }) => (
                    <Row gutter={24}>
                      <Col span={6}>
                        <Form.Item
                          {...restField}
                          name={[name, 0]}
                          fieldKey={[name, 0]}
                          rules={[{ required: true, message: "Please enter a key!" }]}
                        >
                          <Input placeholder="Key" />
                        </Form.Item>
                      </Col>
                      <Col span={6}>
                        <Form.Item
                          {...restField}
                          name={[name, 1, "kind"]}
                          fieldKey={[name, 1, "kind"]}
                          rules={[{ required: true, message: "请选择种类!" }]}
                        >
                          <Select>
                            <Select.Option value={MeasurementKind.UNKNOWN}>未知/未设置</Select.Option>
                            <Select.Option value={MeasurementKind.COUNTER}>计数器</Select.Option>
                            <Select.Option value={MeasurementKind.ABSOLUTE}>绝对值</Select.Option>
                            <Select.Option value={MeasurementKind.GAUGE}>仪表</Select.Option>
                            <Select.Option value={MeasurementKind.STRING}>字符串</Select.Option>
                          </Select>
                        </Form.Item>
                      </Col>
                      <Col span={10}>
                        <Form.Item
                          {...restField}
                          name={[name, 1, "name"]}
                          fieldKey={[name, 1, "name"]}
                          rules={[{ required: true, message: "请输入描述!" }]}
                        >
                          <Input placeholder="名称" />
                        </Form.Item>
                      </Col>
                      <Col span={2}>
                        <MinusCircleOutlined onClick={() => remove(name)} />
                      </Col>
                    </Row>
                  ))}
                  <Form.Item>
                    <Button type="dashed" onClick={() => add()} block icon={<PlusOutlined />}>
                      添加测量值
                    </Button>
                  </Form.Item>
                </>
              )}
            </Form.List>
          </Tabs.TabPane>
        </Tabs>
        <Form.Item>
          <Button type="primary" htmlType="submit">
            提交
          </Button>
        </Form.Item>
      </Form>
    );
  }
}

export default DeviceProfileTemplateForm;
